using System;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Win32;
using System.Xml;
using System.Web.Services.Protocols;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.WebTesting;
using Microsoft.Crm.Application.Outlook.OfflineSync;
using Microsoft.Crm.Sdk.Proxy;
using OfflineSyncTestDriver;
using Microsoft.Crm;

namespace CRM_Perf_BenchMark
{
    /// <summary>
    /// Summary description for GoOfflineUT
    /// </summary>
    [TestClass]
    public class IncrementalGoOfflineUT
    {
        CRMEntity m_user = null;
        string m_userName = string.Empty;
        string m_domain = string.Empty;
        string m_clientKeyPath = string.Empty;
        string m_crmticket = string.Empty;
        string m_CrmServer = string.Empty;
        string m_ApiServer = string.Empty;
        string m_password = string.Empty;
        string m_organization = string.Empty;
        NetworkCredential m_nc = null;
        CrmService m_CrmService = null;

        BcpTransferDataProvider bcp = null;
        PerfSync sync = null;
        XmlDocument resultsDoc = null;
        SyncDataRecorder sdr = null;
       
        private TestContext testContext;

        public IncrementalGoOfflineUT()
        {
            System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:Ctor " + DateTime.Now.ToString());
        }

        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext)
        {
            //this is the directory where all perf results are stored
            string outlookUnitTestDir = Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()) + @"outlooksyncdata\GoOffline";
            if (!Directory.Exists(outlookUnitTestDir))
                Directory.CreateDirectory(outlookUnitTestDir);
        }

        /// <summary>
        /// Initialize() is called once during test execution before
        /// test methods in this test class are executed.
        /// </summary>
        [TestInitialize()]
        public void Initialize()
        {
            System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:Init " + DateTime.Now.ToString());

            bool foundUser = false;

            while (!foundUser)
            {
                try
                {
                    m_user = EntityManager.Instance.GetNextOutlookUser();
                    foundUser = true;
                }

                catch (Exception ex)
                {
                    //we could not get a user
                    System.Diagnostics.Trace.WriteLine(ex.ToString());
                    //lets sleep 5 seconds before we try again
                    System.Threading.Thread.Sleep(5000);
                }
            }

            m_CrmService = new CrmService();

            string[] splits = m_user["domainname"].Split(new char[] { '\\' }, 2, StringSplitOptions.RemoveEmptyEntries);
            if (splits.Length > 1)
            {
                m_domain = splits[0];
                m_userName = splits[1];
            }
            else
            {
                string[] passportusersplit = m_user["passportname"].Split(new char[] { '@' }, 2, StringSplitOptions.RemoveEmptyEntries);
                m_userName = passportusersplit[0];
            }

            m_CrmServer = m_user["CrmServer"];
            m_ApiServer = m_user["ApiServer"];
            m_password = m_user["userpassword"];
            m_organization = m_user["organizationname"];

            if (!SetupWebService())
            {
                System.Diagnostics.Trace.WriteLine("Setup Web Service did not succeed");
            }

            System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:User " + m_userName + " " + DateTime.Now.ToString());
            //set the user credentials for passing into the sync for emulating the outlook client
            //user names can be same for different orgs
            m_clientKeyPath = "SOFTWARE\\Microsoft\\MSCRM" + m_userName + "_" + m_user["organizationid"];

            try
            {
                Guid OrganizationId = new Guid(m_user["organizationid"]);

                //we are going to see here if registry exists, if it does not then we are going to create the registry
                CreateRegistryForUser(m_nc);

                //MSCRMClient just needs one entry RCOffline == 0
                RegistryKey clientRegKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\MSCRMClient", true);
                if (clientRegKey == null)
                {
                    clientRegKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Microsoft\\MSCRMClient");
                    clientRegKey.SetValue("RCOffline", 0, RegistryValueKind.DWord);
                    clientRegKey.SetValue("WebAppUrl", m_CrmServer, RegistryValueKind.String);
                }

                // create transfer data provider - don't need this anymore but registry get set wrong without fix then remove
                bcp = new BcpTransferDataProvider2();
                bcp.NetworkCredential = m_nc;

                sdr = new SyncDataRecorder();
                sync = new PerfSync(sdr);
                sync.ClientKeyPath = m_clientKeyPath;
                sync.Service = m_CrmService;
                sync.MachineName = m_userName;
                sync.UserName = m_userName;
                sync.UserDomain = m_domain;
                sync.NetworkCredential = m_nc;
                sync.OrganizationId = OrganizationId;

                resultsDoc = new XmlDocument();
                resultsDoc.AppendChild(resultsDoc.CreateElement("run_result"));
            }

            catch (System.Web.Services.Protocols.SoapException ex)
            {
                System.Diagnostics.Trace.WriteLine("User " + m_userName + " Soap Exception Detail:" + ex.Detail.InnerXml);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine("User " + m_userName + ":Exception Message:" + ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Cleanup() is called once during test execution after
        /// test methods in this class have executed unless the
        /// corresponding Initialize() call threw an exception.
        /// </summary>
        [TestCleanup()]
        public void Cleanup()
        {
            //we need a valid user to run
            if (m_user == null)
                return;
            System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:Cleanup " + DateTime.Now.ToString());

            EntityManager.Instance.FreeOutlookUser(m_user);

            XmlDocument docNode = resultsDoc.DocumentElement.OwnerDocument;
            XmlElement el;

            try
            {
                el = sdr.SerializeToXML(resultsDoc.DocumentElement.OwnerDocument);
                resultsDoc.DocumentElement.AppendChild(el);

                string fileName = m_userName + "_" + "GoOffline" + "_" + DateTime.Now.ToString() + ".xml";
                fileName = fileName.Replace(" ", "_");
                fileName = fileName.Replace("/", "_");
                fileName = fileName.Replace(":", "_");
                System.Diagnostics.Trace.WriteLine(fileName);
                resultsDoc.Save(ConfigSettings.Default.OutlookSyncDir + "\\GoOffline\\" + fileName);
            }
            catch
            {
            }

            sync = null;
            bcp = null;
            sdr = null;
            resultsDoc = null;
        }

        [TestMethod]
        public void IncrementalGoOfflineTM()
        {
			//we need a valid user to run
            if (m_user == null)
				return;

            try
            {               
                System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:SyncStart User:" + m_userName + " " + DateTime.Now.ToString());
                testContext.BeginTimer("GoOffline");
                sync.Sync(bcp);
                testContext.EndTimer("GoOffline");
                System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:SyncEnd User:" + m_userName + " " + DateTime.Now.ToString());
            }

            catch (SoapException e)
            {
                System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:Exception: User:" + m_userName + " Begin Got SOAP exception");
                System.Diagnostics.Trace.WriteLine(e.Detail.InnerXml);
                System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:Exception: User:" + m_userName + " End Got SOAP exception");
                throw e;
            }

            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:Exception: User:" + m_userName + " Begin Got exception");
                System.Diagnostics.Trace.WriteLine(e.ToString());
                System.Diagnostics.Trace.WriteLine("IncrementalGoOfflineUT:Exception: User:" + m_userName + " End Got exception");
                throw e;
            }
        }

        #region helpers

        public bool SetupWebService()
        {
            //Microsoft.Crm.Sdk.Proxy.CrmService myService = new Microsoft.Crm.Sdk.Proxy.CrmService();
            //currUserToken.CallerId = new Guid(m_user[EntityIDNames.User]);

            m_CrmService.Url = m_ApiServer + "/MSCRMServices/2007/CrmService.asmx";

            
            m_CrmService.UnsafeAuthenticatedConnectionSharing = true;
            m_nc = new NetworkCredential(m_userName, m_password, m_domain);
            m_CrmService.ConnectionGroupName = m_domain + "\\" + m_userName;
            m_CrmService.Credentials = m_nc;
            m_CrmService.Timeout = 1000000;
            
            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.CallerId = new Guid(m_user[EntityIDNames.User]);
            token.OrganizationName = m_organization;            
            token.AuthenticationType = AuthenticationType.AD;
                        
            m_CrmService.CrmAuthenticationTokenValue = token;
            return true;
        }

        private void CreateRegistryForUser(System.Net.NetworkCredential m_nc)
        {
            string userId = m_user["systemuserid"];
            string businessId = m_user["businessunitid"];

            RegistryKey userRegKey = Registry.CurrentUser.OpenSubKey(m_clientKeyPath, true);
            if (userRegKey == null)
                userRegKey = Registry.CurrentUser.CreateSubKey(m_clientKeyPath);

            //we need to create - BusinessId, UserId, ServerUrl
            userRegKey.SetValue("BusinessId", "{" + businessId + "}", RegistryValueKind.String);
            userRegKey.SetValue("UserId", "{" + userId + "}", RegistryValueKind.String);
            userRegKey.SetValue("ServerUrl", m_CrmServer + "/MSCRMServices", RegistryValueKind.String);
            //we need connection string
            userRegKey.SetValue("database", "Provider=SQLOLEDB;Data Source=" + System.Environment.MachineName +"\\CRM;Initial Catalog=MSCRM_MSDE;Integrated Security=SSPI", RegistryValueKind.String);
            userRegKey.SetValue("metabase", "Provider=SQLOLEDB;Data Source=" + System.Environment.MachineName + "\\CRM;Initial Catalog=METABASE;Integrated Security=SSPI", RegistryValueKind.String);
            userRegKey.SetValue("OfflineQueueDBConnection", "Provider=SQLOLEDB;Data Source=" + System.Environment.MachineName + "\\CRM;Initial Catalog=MSCRM_MSDE;Integrated Security=SSPI", RegistryValueKind.String);

            userRegKey.SetValue("OfflineUIDetails", 1, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineUIShowConfirmation", 0, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineRowsBatchSize", 100000, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineMaxRetryCount", 3, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineMinBatchTimeMilliSec", 10000, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineMaxBatchTimeMilliSec", 60000, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineIncreaseRate", 2, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineDecreaseRate", 2, RegistryValueKind.DWord);
            userRegKey.SetValue("OfflineSetOffline", 1, RegistryValueKind.DWord);
        }

        #endregion

        public TestContext TestContext
        {
            set { testContext = value; }
            get { return testContext; }
        }
    }
}
